////
//// Created by LiuYou on 2021/8/11.
////
//
//#include <cstdio>
//#include <cstdlib>
//#include <glob.h>
//#include <sys/syslog.h>
//#include <unistd.h>
//#include <cerrno>
//#include <cstring>
//#include <fcntl.h>
//#include "MediaLib.hpp"
//#include "TBF.hpp"
//#include "../include/Protocol.hpp"
//#include "Server.hpp"
//#include "Channel.hpp"
//#include "ChannelList.hpp"
//
//
//static int PATH_SIZE = 1024;
//static int LINE_BUF_SIZE = 1024;
//static int MP3_BIT_RATE = 64 * 1024;
//
//
//class ChannelContext {
//public:
//    ChannelId channelId;
//    char* description;
//    glob_t mp3Glob;
//    int pos;
//    int fd;
//    off_t offset;
//    TBF* tbf;
//
//};
//
//static ChannelContext channelContext[MAX_CHANNEL_ID + 1];
//
//
//static struct ChannelContext* pathToEntry(const char* path) {
//    // path/desc.text       path/*.mp3
//    char pathString[PATH_SIZE];
//    char lineBuf[LINE_BUF_SIZE];
//
//    static ChannelId currentId = MIN_CHANNEL_ID;
//
//
//    strncpy( pathString, path, PATH_SIZE );
//    strncat( pathString, "/desc.text", PATH_SIZE );
//
//    FILE* fp = fopen( pathString, "r" );
//    if (fp == nullptr) {
//        syslog( LOG_INFO, "%s is not a channel dir(Can't find desc.text)", path );
//        return nullptr;
//    }
//
//    if (fgets( lineBuf, LINE_BUF_SIZE, fp ) == nullptr) {
//        syslog( LOG_INFO, "%s is not a channel dir(Can't get the desc.text)", path );
//        fclose( fp );
//        return nullptr;
//    }
//    fclose( fp );
//
//    struct ChannelContext* me;
//    void* pVoid = malloc( sizeof( *me ));
//    me = static_cast<ChannelContext*>(pVoid);
//    if (me == nullptr) {
//        syslog( LOG_ERR, "malloc(): %s", strerror(errno));
//        return nullptr;
//    }
//
//    me->tbf = tbfInit( MP3_BIT_RATE / 8, MP3_BIT_RATE / 8 * 10 );
//    if (me->tbf == nullptr) {
//        syslog( LOG_ERR, "tbfInit(): %s", strerror(errno));
//        free( me );
//        return nullptr;
//    }
//
//    me->description = strdup( lineBuf );
//
//    strncpy( pathString, path, PATH_SIZE );
//    strncat( pathString, "/*.mp3", PATH_SIZE );
//
//    if (glob( pathString, 0, nullptr, &me->mp3Glob ) != 0) {
//        ++currentId;
//        syslog( LOG_ERR, "%s is not a channel dir(Can't find mp3 file)",
//                path );
//        free( me );
//        return nullptr;
//    }
//
//    me->pos = 0;
//    me->offset = 0;
//    me->fd = open( me->mp3Glob.gl_pathv[me->pos], O_RDONLY );
//    if (me->fd < 0) {
//        syslog( LOG_WARNING, "%s open failed.", me->mp3Glob.gl_pathv[me->pos] );
//        free( me );
//        return nullptr;
//    }
//
//
//    me->channelId = currentId;
//    ++currentId;
//
//    return me;
//
//}
//
//
//// TODO 注意: @_@ 这里二级指针的传参不懂。
//int getChannelList(struct MediaLibEntry** result, int* num) {
//    for (int i = 0; i < MAX_CHANNEL_ID + 1; ++i) {
//        channelContext[i].channelId = -1;
//    }
//
//    char path[PATH_SIZE];
//    snprintf( path, PATH_SIZE, "%s/*", serverConfig.mediaDir );
//
//    glob_t globResult;
//    if (glob( path, 0, nullptr, &globResult )) {
//        return -1;
//    }
//
//    struct MediaLibEntry* ptr;
//    void* pVoid = malloc( sizeof( MediaLibEntry ) * globResult.gl_pathc );
//    ptr = static_cast<MediaLibEntry*>(pVoid);
//    if (ptr == nullptr) {
//        syslog( LOG_ERR, "malloc() error." );
//        exit( 1 );
//    }
//
//    int index = 0;
//    for (int i = 0; i < globResult.gl_pathc; ++i) {
//        // globResult.gl_pathv[i] -> "/var/media/ch1"
//        ChannelContext* res = pathToEntry( globResult.gl_pathv[i] );
//        if (res != nullptr) {
//            syslog( LOG_DEBUG, "pathToEntry() returned: %d %s.",
//                    res->channelId, res->description );
//            memcpy( channelContext + res->channelId, res, sizeof( *res ));
//            ptr[index].channelId = res->channelId;
//            ptr[index].description = strdup( res->description );
//            ++index;
//        }
//    }
//
//    void* pVoid1 = realloc( ptr, sizeof( MediaLibEntry ) * index );
//    *result = static_cast<MediaLibEntry*>(pVoid1);
//    if (*result == nullptr) {
//        syslog( LOG_ERR, "realloc() failed." );
//    }
//
//    *num = index;
//
//    return 0;
//}
//
//int freeChannelList(MediaLibEntry* ptrMediaLibEntry) {
//    free( ptrMediaLibEntry );
//    return 0;
//}
//
//static int openNext(ChannelId channelId) {
//    for (int i = 0; i < channelContext[channelId].mp3Glob.gl_pathc; ++i) {
//        ++(channelContext[channelId].pos);
//        if (channelContext[channelId].pos == channelContext[channelId].mp3Glob.gl_pathc) {
////            channelContext[channelId].pos = 0;
//            return -1;
//            break;
//        }
//        close( channelContext[channelId].fd );
//        channelContext[channelId].fd =
//                open( channelContext[channelId].mp3Glob.gl_pathv[channelContext[channelId].pos], O_RDONLY );
//        if (channelContext[channelId].fd < 0) {
//            syslog( LOG_WARNING, "open(%s): %s",
//                    channelContext[channelId].mp3Glob.gl_pathv[channelContext[channelId].pos],
//                    strerror(errno));
//        } else {
//            channelContext[channelId].offset = 0;
//            return 0;
//        }
//    }
//    syslog( LOG_ERR, "None of mp3 in channel %d is available.",
//            channelId );
//}
//
//int readChannelList(ChannelId channelId, void* buf, size_t size) {
//    tbfFetchToken( channelContext[channelId].tbf, size );
//
//    size_t tbfSize = 0;
//    ssize_t len;
//    while (true) {
//        len = pread( channelContext[channelId].fd, buf, tbfSize, channelContext[channelId].offset );
//        if (len < 0) {
//            syslog( LOG_WARNING, "meida file %s pread(): %s",
//                    channelContext[channelId].mp3Glob.gl_pathv[channelContext[channelId].pos],
//                    strerror(errno));
//            openNext( channelId );
//            // 不下面使用的原因是逻辑有点复杂。
////                        if (openNext( channelId ) < 0) {
////                            syslog( LOG_ERR, "channel %d: There is no succeed open.",
////                                    channelId );
////                        }
//        } else if (len == 0) {
//            syslog( LOG_DEBUG, "media file %s is over.",
//                    channelContext[channelId].mp3Glob.gl_pathv[channelContext[channelId].pos] );
//            openNext( channelId );
//            // 不下面使用的原因是逻辑有点复杂。
////                        if (openNext( channelId ) < 0) {
////                            syslog( LOG_ERR, "channel: %d: There is no succeed open.",
////                                    channelId );
////                        }
//        } else {
//            channelContext[channelId].offset += len;
//            syslog( LOG_DEBUG, "读到了数据" );
//
//            break;
//        }
//    }
//    if (tbfSize - len > 0) {
//        tbfReturnToken( channelContext[channelId].tbf,
//                        tbfSize - len );
//    }
//
//    return len;
//}
//







#include <stdio.h>
#include <stdlib.h>
#include <glob.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#include <error.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "Server.hpp"
#include "MediaLib.hpp"
#include "../include/Protocol.hpp"
#include "TBF.hpp"
//#define DEBUG

#define PATHSIZE 1024
#define LINEBUFSIZE 1024
#define MP3_BITRATE 64*1024 //correct bps:128*1024

using chnid_t = uint8_t;
using mytbf_t = TBF;

struct channel_context_st {
    chnid_t chnid;
    char* desc;
    glob_t mp3glob;
    int pos;  //cur song
    int fd;  //file descriptor
    off_t offset;
    mytbf_t* tbf;
};

static struct channel_context_st channel[MAX_CHANNEL_ID + 1];

static struct channel_context_st* path2entry(const char* path) {
    // path/desc.text  path/*.mp3
    syslog( LOG_INFO, "current path :%s\n", path );
    char pathstr[PATHSIZE] = {'\0'};
    char linebuf[LINEBUFSIZE];
    FILE* fp;
    struct channel_context_st* me;
    static chnid_t curr_id = MIN_CHANNEL_ID;
    strcat( pathstr, path );
    strcat( pathstr, "/desc.text" );
    fp = fopen( pathstr, "r" );
    syslog( LOG_INFO, "channel dir:%s\n", pathstr );
    if (fp == NULL) {
        syslog( LOG_INFO, "%s is not a channel dir(can't find desc.txt)", path );
        return NULL;
    }
    if (fgets( linebuf, LINEBUFSIZE, fp ) == NULL) {
        syslog( LOG_INFO, "%s is not a channel dir(cant get the desc.text)", path );
        fclose( fp );
        return NULL;
    }
    fclose( fp );
    me = static_cast<channel_context_st*>(malloc( sizeof( *me )));
    if (me == NULL) {
        syslog( LOG_ERR, "malloc():%s", strerror(errno));
        return NULL;
    }

    me->tbf = tbfInit( MP3_BITRATE / 8, MP3_BITRATE / 8 * 5 );
    if (me->tbf == NULL) {
        syslog( LOG_ERR, "mytbf_init():%s", strerror(errno));
        free( me );
        return NULL;
    }
    me->desc = strdup( linebuf );
    strncpy( pathstr, path, PATHSIZE );
    strncat( pathstr, "/*.mp3", PATHSIZE );
    if (glob( pathstr, 0, NULL, &me->mp3glob ) != 0) {
        curr_id++;
        syslog( LOG_ERR, "%s is not a channel dir(can not find mp3 files", path );
        free( me );
        return NULL;
    }
    me->pos = 0;
    me->offset = 0;
    me->fd = open( me->mp3glob.gl_pathv[me->pos], O_RDONLY );
    if (me->fd < 0) {
        syslog( LOG_WARNING, "%s open failed.", me->mp3glob.gl_pathv[me->pos] );
        free( me );
        return NULL;
    }
    me->chnid = curr_id;
    curr_id++;
    return me;
}

int getChannelList(struct MediaLibEntry** result, int* resnum) {
    /*variable*/
    int num = 0;
    int i = 0;
    char path[PATHSIZE];
    glob_t globres;
    struct MediaLibEntry* ptr;
    struct channel_context_st* res;

    for (int i = 0; i < MAX_CHANNEL_ID; i++) {
        channel[i].chnid = -1;
    }
    snprintf( path, PATHSIZE, "%s/*", serverConfig.mediaDir );
#ifdef DEBUG
    printf("current path:%s\n", path);
#endif
    if (glob( path, 0, NULL, &globres )) {
        return -1;
    }
#ifdef DEBUG
    printf("globres.gl_pathv[0]:%s\n", globres.gl_pathv[0]);
    printf("globres.gl_pathv[1]:%s\n", globres.gl_pathv[1]);
    printf("globres.gl_pathv[2]:%s\n", globres.gl_pathv[2]);
#endif
    ptr = static_cast<struct MediaLibEntry*>(malloc( sizeof( struct MediaLibEntry ) * globres.gl_pathc ));
    if (ptr == NULL) {
        syslog( LOG_ERR, "malloc() error." );
        exit( 1 );
    }
    for (i = 0; i < globres.gl_pathc; i++) {
        //globres.gl_pathv[i] -->"/var/media/ch1"
        res = path2entry( globres.gl_pathv[i] );//path-->record
        if (res != NULL) {
            syslog( LOG_ERR, "path2entry() return : %d %s.", res->chnid, res->desc );
            memcpy( channel + res->chnid, res, sizeof( *res ));
            ptr[num].channelId = res->chnid;
            ptr[num].description = strdup( res->desc );
            num++;
        }
    }
    *result = static_cast<MediaLibEntry*>(realloc( ptr, sizeof( struct MediaLibEntry ) * num ));
    if (*result == NULL) {
        syslog( LOG_ERR, "realloc() failed." );
    }
    *resnum = num;
    return 0;


}

int freeChannelList(struct MediaLibEntry* ptr) {
    free( ptr );
    return 0;
}

//当前是失败了或者已经读取完毕才会调用open_next
static int open_next(chnid_t chnid) {
    //加入循环是为了防止每一首歌都打开失败，尽量都试着打开一次
    for (int i = 0; i < channel[chnid].mp3glob.gl_pathc; i++) {
        channel[chnid].pos++;
        //can open any file in channel[chnid].mp3glob.gl_pathv
        //所有的歌曲都没有打开
        if (channel[chnid].pos == channel[chnid].mp3glob.gl_pathc) {
            //channel[chnid].pos = 0;//再来一次
            return -1;
            break;//最后一首歌已经打开完毕，结束
        }
        close( channel[chnid].fd );
        channel[chnid].fd = open( channel[chnid].mp3glob.gl_pathv[channel[chnid].pos], O_RDONLY );//对应频道的歌名
        //如果打开还是失败
        if (channel[chnid].fd < 0) {
            syslog( LOG_WARNING, "open(%s):%s", channel[chnid].mp3glob.gl_pathv[chnid], strerror(errno));
        } else//success
        {
            channel[chnid].offset = 0;
            return 0;
        }
    }
    syslog( LOG_ERR, "None of mp3 in channel %d id available.", chnid );

}

//从每个频道中读取内容，将当前播放的歌曲待发送的数据写到buf，实际大小为size
int readChannelList(chnid_t chnid, void* buf, size_t size) {
    //int readChannelList(ChannelId channelId, void* buf, size_t size) {
    int tbfsize;
    int len;
    int next_ret = 0;
    //get token number
    tbfsize = tbfFetchToken( channel[chnid].tbf, size );
//    syslog( LOG_INFO, "current tbf():%d", mytbf_checktoken( channel[chnid].tbf ));


    while (1) {
        len = pread( channel[chnid].fd, buf, tbfsize, channel[chnid].offset );
        /*current song open failed*/
        if (len < 0) {
            //当前这首歌可能有问题，错误不至于退出，读取下一首歌
            syslog( LOG_WARNING, "media file %s pread():%s", channel[chnid].mp3glob.gl_pathv[channel[chnid].pos],
                    strerror(errno));
            open_next( chnid );
        } else if (len == 0) {
            syslog( LOG_DEBUG, "media %s file is over", channel[chnid].mp3glob.gl_pathv[channel[chnid].pos] );
#ifdef DEBUG
            printf("current chnid :%d\n", chnid);
#endif
            next_ret = open_next( chnid );
            break;
        } else/*len > 0*///真正读取到了数据
        {
            channel[chnid].offset += len;
            syslog( LOG_DEBUG, "epoch : %f", (channel[chnid].offset) / (16 * 1000 * 1.024));
            break;
        }
    }
    //remain some token
    if (tbfsize - len > 0)
        tbfReturnToken( channel[chnid].tbf, tbfsize - len );
    return len;//返回读取到的长度


}


